<template>
  <div class="editor">
    <div class="main">
      <codemirror
        v-model="code"
        :style="{
          width: preview ? '50%' : '100%',
          height: '100%',
          backgroundColor: '#fff',
          color: '#333'
        }"
        :placeholder="config.placeholder"
        :extensions="extensions"
        :autofocus="config.autofocus"
        :disabled="config.disabled"
        :indent-with-tab="config.indentWithTab"
        :tab-size="config.tabSize"
        :auto-destroy="config.autoDestroy"
        @update="handleStateUpdate"
        @ready="handleReady"
        @focus="log('focus', $event)"
        @blur="log('blur', $event)"
      />
      <pre
        v-if="preview"
        class="code"
        :style="{ height: '100%', width: preview ? '50%' : '0px' }"
        >{{ code }}</pre
      >
    </div>
    <div class="footer px-2 flex items-center whitespace-nowrap flex-row justify-between">
      <div class="action_box text-slate-600 flex items-center justify-end flex-row border-slate-300 border text-xs tracking-widest leading-4 rounded">
        <div class="flex items-center whitespace-nowrap p-2 border-solid cursor-pointer border-r border-slate-300" @click="togglePreview">预览<EyeOutlined v-if="preview" /><EyeInvisibleOutlined v-else /></div>
        <div class="flex items-center whitespace-nowrap p-2 border-solid cursor-pointer border-r border-slate-300" @click="handleUndo">撤销</div>
        <div class="flex items-center whitespace-nowrap p-2 border-solid cursor-pointer border-slate-300" @click="handleRedo">恢复</div>
      </div>
      <div class="tips flex items-center whitespace-nowrap justify-end flex-row text-slate-600 text-xs">
        <span class="ml-4">缩进: <cite class="text-fuchsia-400">{{ config.tabSize }}</cite></span>
        <span class="ml-4">字符长度: <cite class="text-fuchsia-400">{{ state.length }}</cite></span>
        <span class="ml-4">行数: <cite class="text-fuchsia-400">{{ state.lines }}</cite></span>
        <span class="ml-4">当前行: <cite class="text-fuchsia-400">{{ state.cursor }}</cite></span>
        <span class="ml-4">选中: <cite class="text-fuchsia-400">{{ state.selected }}</cite></span>
      </div>
    </div>
  </div>
</template>

<script lang="ts">
  import { defineComponent, reactive, shallowRef, computed, watch, onMounted } from 'vue'
  import { EditorView, ViewUpdate } from '@codemirror/view'
  import { redo, undo } from '@codemirror/commands'
  import { Button } from 'ant-design-vue'
  import  * as codeMirrorView from 'vue-codemirror'

  export default defineComponent({
    name: 'vue-codemirror-example',
    title: 'Web IDE example',
    url: import.meta.url,
    props: {
      config: {
        type: Object,
        required: true
      },
      code: {
        type: String,
        required: true
      },
      theme: [Object, Array],
      language: Function
    },
    components: {
      Codemirror:codeMirrorView.Codemirror
    },
    setup(props) {
      const log = console.log
      const code = shallowRef(props.code)
      const extensions = computed(() => {
        const result = []
        if (props.language) {
          result.push(props.language())
        }
        if (props.theme) {
          result.push(props.theme)
        }
        return result
      })

      const preview = shallowRef(false)
      const togglePreview = () => {
        preview.value = !preview.value
      }

      const cmView = shallowRef<EditorView>()
      const handleReady = ({ view }: any) => {
        cmView.value = view
      }

      // https://github.com/codemirror/commands/blob/main/test/test-history.ts
      const handleUndo = () => {
        undo({
          state: cmView.value!.state,
          dispatch: cmView.value!.dispatch
        })
      }

      const handleRedo = () => {
        redo({
          state: cmView.value!.state,
          dispatch: cmView.value!.dispatch
        })
      }

      const state = reactive({
        lines: null as null | number,
        cursor: null as null | number,
        selected: null as null | number,
        length: null as null | number
      })

      const handleStateUpdate = (viewUpdate: ViewUpdate) => {
        // selected
        const ranges = viewUpdate.state.selection.ranges
        state.selected = ranges.reduce((plus, range) => plus + range.to - range.from, 0)
        state.cursor = ranges[0].anchor
        // length
        state.length = viewUpdate.state.doc.length
        state.lines = viewUpdate.state.doc.lines
        // log('viewUpdate', viewUpdate)
      }

      onMounted(() => {
        watch(
          () => props.code,
          (_code) => {
            code.value = _code
          }
        )
      })

      return {
        log,
        code,
        extensions,
        preview,
        state,
        togglePreview,
        handleReady,
        handleStateUpdate,
        handleRedo,
        handleUndo
      }
    }
  })
</script>
<style lang="scss" scoped>
  .editor {
    height: 100%;
    overflow: hidden;
    .main {
      display: flex;
      width: 100%;
      height: calc(100% - 96px);
      overflow: auto;
      :deep(.cm-scroller){
        @include scrollbar($width: 10px, $height: 10px);
      }
      .code {
        width: 30%;
        height: 100px;
        margin: 0;
        padding: 0.4em;
        overflow: scroll;
        border-left: 1px solid orange;
        font-family: monospace;
        color: #fff;
        background-color: rgba($color: #000000, $alpha: 0.8);
        @include scrollbar($width: 10px, $height: 10px);
      }
    }
    .footer {
        height: 48px;
        width: 100%;
        @include font_color;
        background-color: #fff;
        text-align: left;
        border-bottom: 1px solid rgba(5, 5, 5, 0.06);
        display: flex;
        justify-content: space-between;
        align-items: center;
        .action_box{
          background-color: rgba(0,0,0,.02);
        }
        .tips{
          cite{
            font-style: unset;
          }
        }
    }
  }
</style>

